#include "stdafx.h"
#include "torus_game.h"
#include "controller/input_controller.h"


torus_game& torus_game::get_instance()
{
	static torus_game instance;
	return instance;
}

torus_game::torus_game()
	: score(0)
	, game_end(false)
{
	reset();
	generate_block();
}

torus_game::~torus_game()
{
}

void torus_game::reset()
{
	for (int i = 0; i < GROUND_MAX_X; i++)
	{
		for (int j = 0; j < GROUND_MAX_Y; j++)
			matrix[i][j] = NULL;
	}
}

bool torus_game::is_block_movable(block& b, int mx, int my)
{
	bool ret = true;
	vec2 npos;

	for (int i = 0; i < BLOCK_SIZE; i++)
	{
		if (b.points_able[i])
		{
			npos =	b.points[i] + vec2(mx, my);
			block::refine(npos);
			ret	 =	ret && (npos.x >= 0 && npos.x < GROUND_MAX_X) //in-range
						&& (b.contains(npos) || matrix[npos.x][npos.y] == NULL); //is own point or free point
		}
	}

	return ret;
}

bool torus_game::is_block_rotable(block& b)
{
	block preview = b;
	preview.rotate_preview();

	//check if placable
	bool ret = true;

	for (int i = 0; i < BLOCK_SIZE; i++)
	{
		if (preview.points_able[i])
		{
			ret	 =	ret && (preview.points[i].x >= 0 && preview.points[i].x < GROUND_MAX_X) //in-range
						&& (matrix[preview.points[i].x][preview.points[i].y] == NULL ||		//free space to move there
							matrix[preview.points[i].x][preview.points[i].y] == &b);		//or its the block b
		}
	}

	return ret;
}

bool torus_game::is_block_placable(block& b)
{
	bool ret = true;

	for (int i = 0; i < BLOCK_SIZE; i++)
	{
		if (b.points_able[i])
		{
			ret	 =	ret && (b.points[i].x >= 0 && b.points[i].x < GROUND_MAX_X) //in-range
						&& (matrix[b.points[i].x][b.points[i].y] == NULL); //free space to move there
		}
	}

	return ret;
}

void torus_game::update_matrix()
{
	reset();
	//update all point
	for (block_list::iterator it = b_list.begin(); it != b_list.end(); it++)
	{
		block& b = *(*it);
		for (int i = 0; i < BLOCK_SIZE; i++)
		{
			if (b.points_able[i])
				matrix[b.points[i].x][b.points[i].y] = &b;
		}
	}
}

void torus_game::update(float delta_time)
{
	if (game_end)
		return;

	//
	static float time_elapsed = 0.0f;
	time_elapsed += delta_time;

	//move active block according to controller
	if (INPUT.move_y > 1.0f)
	{
		if ( is_block_movable(*active_block, 0, 1) )
			active_block->move(0, 1);
		INPUT.reset_y();
	}
	if (INPUT.move_y < -1.0f)
	{
		if ( is_block_movable(*active_block, 0, -1) )
			active_block->move(0, -1);
		INPUT.reset_y();
	}
	if (INPUT.move_x > 1.0f)
	{
		if ( is_block_movable(*active_block, 1, 0) )
			active_block->move(1, 0);
		INPUT.reset_x();
	}
	if (INPUT.move_r > 1.0f )
	{
		if ( is_block_rotable(*active_block) )
			active_block->rotate();
		INPUT.reset_r();
	}

	if (active_block->update())
		update_matrix();

	//move active block down every DROP_INTERVAL second(s). if it cant be moved then generate new active block
	if (time_elapsed > DROP_INTERVAL)
	{
		time_elapsed = 0.0f;
		if (is_block_movable(*active_block, 1, 0))
		{
			active_block->move(1, 0);
			active_block->update();
		}
		else
		{
			//time to check if line full, and generate new block (this may happen several times)
			while (remove_full_line());
			generate_block();
		}
		update_matrix();
	}
}

void torus_game::render()
{
	debug_print();
}

void torus_game::debug_print()
{
	//render to console =))
	system("cls");

	if (game_end)
	{
		printf("Your score: %d!\n", score);
		return;
	}

	for (int i = 0; i < GROUND_MAX_X; i++)
	{
		for (int j = 0; j < GROUND_MAX_Y; j++)
		{
			if (j == 0)
				printf("|");
			if (matrix[i][j] == NULL)
				printf(" ");
			else
				printf("%d", (matrix[i][j])->color);
			if (j == GROUND_MAX_Y - 1)
				printf("|");
		}
		printf("\n");
	}
	printf("+");
	for (int i = 0; i < GROUND_MAX_Y; i++)
		printf("-");
	printf("+");
}

void torus_game::generate_block()
{
	block::block_shape shape = (block::block_shape) ((unsigned int)rand() % block::MAX_SHAPE);
	block::block_color color = (block::block_color) ((unsigned int)rand() % (block::MAX_COLOR - 1) + 1);

	block* b = new block(vec2(1, GROUND_MAX_Y / 2), shape, color);

	if (is_block_placable(*b))
	{
		b_list.push_back(b);
		active_block = b;
	}
	else
	{
		game_end = true;
		delete b;
	}
}

bool torus_game::remove_full_line()
{
	//check for a full line and remove it, return true if exists such line.
	for (int i = GROUND_MAX_X - 1; i > 0; i--)
	{
		bool line_full = true;
		for (int j = 0; j < GROUND_MAX_Y; j++)
		{
			if ( matrix[i][j] == NULL )
				line_full = false;
		}

		//remove points of the full line
		if (line_full)
		{
			//if a removed point belongs to a block, tell this block to disable that point
			for (int j = 0; j < GROUND_MAX_Y; j++)
			{
				if ( (matrix[i][j])->remove_point(i, j) ) 
				{
					b_list.remove(matrix[i][j]);
					delete (matrix[i][j]);
				}
			}
							
			//for all blocks above this line, move them 1 point lower
			for (block_list::iterator it = b_list.begin(); it != b_list.end(); it++)
			{
				block& b = *(*it);
				if (b.pos.x <= i)
				{
					b.move(1, 0);
					b.update();
				}
			}

			//update matrix because some have changed...
			update_matrix();

			//score for player
			score += 10;
			return true;
		}
	}
	return false;
}

void torus_game::clean_up()
{
	for (block_list::iterator it = b_list.begin(); it != b_list.end(); it++)
	{
		block* b = *it;
		if (b) 
			delete b;
	}
	b_list.clear();
}